/**
 * $Revision$
 * $Date$
 *
 * Copyright (C) https://gitee.com/baibaiclouds/platform . All rights reserved.
 * <p>
 * This software is the confidential and proprietary information of .
 * You shall not disclose such Confidential Information and shall use it only
 * in accordance with the terms of the agreements you entered into with .
 * 
 * Modified history:
 *   baibai  2021年5月3日 下午11:48:31  created
 */
package com.desktop.web.service.node;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.desktop.web.core.exception.BusinessException;
import com.desktop.web.core.utils.RequestUtil;
import com.desktop.web.service.config.ConfigService;
import com.desktop.web.service.log.LogService;
import com.desktop.web.service.role.RoleService;
import com.desktop.web.service.user.UserService;
import com.desktop.web.uda.entity.Node;
import com.desktop.web.uda.entity.Role;
import com.desktop.web.uda.entity.RoleNode;
import com.desktop.web.uda.mapper.NodeMapper;

/**
 * 
 *
 * @author baibai
 */
@Service
public class NodeService {

    public static final Long ROOT_NODE_ID = 0L;

    @Autowired
    private NodeMapper nodeMapper;

    @Autowired
    private RoleService roleService;

    @Autowired
    private LogService logService;

    @Autowired
    private ConfigService configService;

    /**
     * 初始化顶级节点和未知节点
     * 
     * @param teamName
     */
    public void initNode(String teamName) {
        Node node = new Node();
        node.setTitle(teamName);
        node.setPid(ROOT_NODE_ID);
        node.setSort(0L);
        nodeMapper.insert(node);

        Node update = new Node();
        update.setId(node.getId());
        update.setPath(ROOT_NODE_ID + "," + node.getId());
        nodeMapper.updateById(update);

        Node unknow = new Node();
        unknow.setPid(node.getId());
        unknow.setSort(Long.MAX_VALUE);
        unknow.setTitle("未知设备");
        nodeMapper.insert(unknow);
        Node udpateUnknow = new Node();
        udpateUnknow.setId(unknow.getId());
        udpateUnknow.setPath(ROOT_NODE_ID + "," + node.getId() + "," + unknow.getId());
        nodeMapper.updateById(udpateUnknow);

        configService.addValue("root_node_id", update.getId());
        configService.addValue("unknow_node_id", unknow.getId());
    }

    /**
     * 获取设备节点树
     * 
     * @return
     */
    public Object getNodeTreeCuruser() {
        List<Map<String, Object>> retList = new ArrayList<Map<String, Object>>();
        List<Long> nodeIds = this.getNodeIds();
        if (nodeIds.isEmpty()) {
            return retList;
        }

        nodeIds.forEach((item) -> {
            Map<String, Object> tempnode = this.convertNode(nodeMapper.selectById(item));
            tempnode.put("pid", ROOT_NODE_ID);
            List<Map<String, Object>> templist = this.getNodeTreeListByPid(item);
            tempnode.put("children", templist);
            retList.add(tempnode);
        });

        return retList;
    }

    private List<Map<String, Object>> getNodeTreeListByPid(Long pid) {
        List<Map<String, Object>> retList = new ArrayList<Map<String, Object>>();

        Node entity = new Node();
        entity.setPid(pid);
        QueryWrapper<Node> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByAsc("sort");
        queryWrapper.setEntity(entity);

        List<Node> nodeList = nodeMapper.selectList(queryWrapper);

        for (Node item : nodeList) {
            Map<String, Object> itemmap = this.convertNode(item);
            retList.add(itemmap);
            List<Map<String, Object>> citemList = this.getNodeTreeListByPid(item.getId());
            if (citemList.isEmpty()) {
                continue;
            }
            itemmap.put("children", citemList);
        }

        return retList;
    }

    private Map<String, Object> convertNode(Node node) {
        Map<String, Object> temp = new HashMap<>();
        temp.put("id", node.getId());
        temp.put("label", node.getTitle());
        temp.put("pid", node.getPid());
        return temp;
    }

    private List<Long> getNodeIds() {
        List<Long> retIds = new ArrayList<Long>();
        List<Role> roles = roleService.getRoleListByUid(RequestUtil.getUid());
        if (roles == null || roles.isEmpty()) {
            return retIds;
        }

        boolean isDefaultRole = false;
        for (Role item : roles) {
            if (item.getId().equals(UserService.ADMIN_ROLE_ID)) {
                isDefaultRole = true;
                break;
            }
        }

        if (isDefaultRole) {
            Long defaultRoleId = configService.getRootNodeId();
            retIds.add(defaultRoleId);
            return retIds;
        }

        List<RoleNode> roleNodeList = new ArrayList<RoleNode>();
        roles.forEach((item) -> {
            List<RoleNode> templist = roleService.getRoleNodeListByRoleid(item.getId());
            roleNodeList.addAll(templist);
        });

        List<Node> roleList = new ArrayList<Node>();
        roleNodeList.forEach((item) -> {
            Node temprole = this.getNodeById(item.getNodeId());
            if (temprole == null) {
                return;
            }
            roleList.add(temprole);
        });

        Collections.sort(roleList, new Comparator<Node>() {
            public int compare(Node one, Node two) {
                int hits0 = one.getPath().length();
                int hits1 = two.getPath().length();
                if (hits1 > hits0) {
                    return 1;
                } else if (hits1 == hits0) {
                    return 0;
                } else {
                    return -1;
                }
            };
        });

        for (int i = 0; i < roleList.size(); i++) {
            Node inode = roleList.get(i);
            for (int k = roleList.size() - 1; k >= 0; k--) {
                Node knode = roleList.get(k);
                if (inode.getId().equals(knode.getId())) {
                    continue;
                }

                if (inode.getPath().indexOf(knode.getPath()) == 0) {
                    roleList.remove(i);
                    i--;
                    break;
                }
            }
        }

        roleList.forEach((item) -> {
            retIds.add(item.getId());
        });

        return retIds;
    }

    /**
     * 获取节点路径
     * 
     * @param nodeId 父节点id
     * @return
     */
    private String makeNodePath(long nodeId) {
        Node node = nodeMapper.selectById(nodeId);
        if (node == null) {
            throw new BusinessException("参数非法");
        }

        Node pnode = nodeMapper.selectById(node.getPid());
        if (pnode == null) {
            throw new BusinessException("参数非法");
        }

        return pnode.getPath() + "," + nodeId;
    }

    /**
     * 添加节点
     * 
     * @param pid
     * @param title
     * @return
     */
    public Node addNode(Long pid, String title) {

        Long unknowNodeId = configService.getUnknowNodeId();
        if (pid.equals(unknowNodeId)) {
            throw new BusinessException("此节点不能操作");
        }

        Node pnode = nodeMapper.selectById(pid);
        if (pnode == null) {
            throw new BusinessException("参数非法");
        }

        Node node = new Node();
        node.setPid(pid);
        node.setTitle(title);
        nodeMapper.insert(node);

        node.setPath(this.makeNodePath(node.getId()));
        node.setSort(node.getId());
        nodeMapper.updateById(node);

        logService.addLogCuruser("设备节点管理", "添加节点成功，节点名称:{}", title);
        return node;
    }

    /**
     * 判断是否有权限操作节点
     * 
     * @param nodeId
     */
    public void checkNodeByCuruser(Long nodeId) {
        checkNodeByNodeid(RequestUtil.getUid(), nodeId);
    }

    /**
     * 判断是否有权限
     * 
     * @param uid
     * @param nodeId
     */
    public void checkNodeByNodeid(Long uid, Long nodeId) {
        Node tempNode = this.getNodeById(nodeId);
        if (tempNode == null) {
            throw new BusinessException("无权限");
        }

        List<Role> roles = roleService.getRoleListByUid(uid);
        if (roles == null || roles.isEmpty()) {
            throw new BusinessException("无权限");
        }

        for (Role item : roles) {
            if (item.getId().equals(UserService.ADMIN_ROLE_ID)) {
                return;
            }
        }

        List<RoleNode> roleNodeList = new ArrayList<RoleNode>();
        roles.forEach((item) -> {
            List<RoleNode> templist = roleService.getRoleNodeListByRoleid(item.getId());
            roleNodeList.addAll(templist);
        });

        for (RoleNode item : roleNodeList) {
            if (tempNode.getPath().indexOf(item.getNodeId().toString()) != -1) {
                return;
            }
        }

        throw new BusinessException("无权限");
    }

    /**
     * 更新节点名称
     * 
     * @param id
     * @param newname
     */
    public void renameNode(Long id, String newname) {

        Long unknowNodeId = configService.getUnknowNodeId();
        if (id.equals(unknowNodeId)) {
            throw new BusinessException("此节点不能操作");
        }

        Node tempnode = nodeMapper.selectById(id);
        if (tempnode == null) {
            throw new BusinessException("参数非法");
        }

        if (StringUtils.isEmpty(newname)) {
            throw new BusinessException("参数非法");
        }

        Node update = new Node();
        update.setId(id);
        update.setTitle(newname);
        nodeMapper.updateById(update);

        logService.addLogCuruser("设备节点管理", "更新节点成功，旧节点名称：{},节点名称:{}", tempnode.getTitle(), newname);
    }

    /**
     * 删除节点
     * 
     * @param id
     */
    public void deleteNode(Long id) {

        Long unknowNodeId = configService.getUnknowNodeId();
        if (id.equals(unknowNodeId)) {
            throw new BusinessException("此节点不能操作");
        }

        if (id.equals(NodeService.ROOT_NODE_ID)) {
            throw new BusinessException("顶级节点无法删除");
        }

        Node tempnode = nodeMapper.selectById(id);
        if (tempnode == null) {
            throw new BusinessException("参数非法");
        }

        Node entity = new Node();
        entity.setPid(id);
        QueryWrapper<Node> queryWrapper = new QueryWrapper<>();
        queryWrapper.setEntity(entity);

        int count = nodeMapper.selectCount(queryWrapper);
        if (count > 0) {
            throw new BusinessException("请先删除子节点");
        }

        nodeMapper.deleteById(id);
        logService.addLogCuruser("设备节点管理", "删除节点成功，节点名称:{}", tempnode.getTitle());
    }

    /**
     * 移动节点
     * 
     * @param nodeId
     * @param targetId
     * @param sortIds
     */
    public void moveNode(Long nodeId, Long targetId, List<Long> sortIds) {

        Long unknowNodeId = configService.getUnknowNodeId();
        if (nodeId.equals(unknowNodeId)) {
            throw new BusinessException("此节点不能操作");
        }

        Node node = nodeMapper.selectById(nodeId);
        if (node.getPid().equals(NodeService.ROOT_NODE_ID)) {
            throw new BusinessException("顶级节点无法移动");
        }

        Node targetNode = nodeMapper.selectById(targetId);

        Node update = new Node();
        update.setId(nodeId);
        update.setPid(targetId);
        nodeMapper.updateById(update);

        String path = this.makeNodePath(nodeId);

        update = new Node();
        update.setId(nodeId);
        update.setPath(path);
        nodeMapper.updateById(update);

        Node updateSort = new Node();
        for (int i = 0; i < sortIds.size(); i++) {
            updateSort.setId(sortIds.get(i));
            updateSort.setSort(Long.valueOf((i + 1)));
            nodeMapper.updateById(updateSort);
        }

        logService.addLogCuruser("设备节点管理", "移动节点成功，节点名称:{}，节点移动到:{}，排序:{}", node.getTitle(), targetNode.getTitle(), sortIds);
    }

    /**
     * 根据id获取设备节点信息
     * 
     * @param nodeId
     * @return
     */
    public Node getNodeById(Long nodeId) {
        return nodeMapper.selectById(nodeId);
    }

    /**
     * 根据节点id获取子节点列表
     * 
     * @param nodeId
     * @return
     */
    public List<Node> getListNodeByNodeid(Long nodeId) {
        QueryWrapper<Node> queryWrapper = new QueryWrapper<Node>().apply("find_in_set({0} ,path)", nodeId.toString());
        return nodeMapper.selectList(queryWrapper);
    }

}
